package com.wyz.saas.common.utils

import android.annotation.TargetApi
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.os.Build
import java.io.*

object BitmapUtil {
    /**
     * A default size to use to increase hit rates when the required size isn't defined.
     * Currently 64KB.
     */
    private const val DEFAULT_BUFFER_SIZE = 64 * 1024

    /**
     * 创建一个图片处理Options
     */
    fun createOptions(): BitmapFactory.Options {
        return BitmapFactory.Options()
    }

    /**
     * 把一个[BitmapFactory.Options]进行参数复原操作，
     * 避免重复创建新的 [BitmapFactory.Options]
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    fun resetOptions(options: BitmapFactory.Options) {
        options.inTempStorage = null
        options.inDither = false
        options.inScaled = false
        options.inSampleSize = 1
        options.inPreferredConfig = null
        options.inJustDecodeBounds = false
        options.inDensity = 0
        options.inTargetDensity = 0
        options.outWidth = 0
        options.outHeight = 0
        options.outMimeType = null
        if (Build.VERSION_CODES.HONEYCOMB <= Build.VERSION.SDK_INT) {
            options.inBitmap = null
            options.inMutable = true
        }
    }

    /**
     * 获取图片的真实后缀
     */
    fun getExtension(filePath: String): String {
        val options = createOptions()
        options.inJustDecodeBounds = true
        BitmapFactory.decodeFile(filePath, options)
        val mimeType = options.outMimeType
        return mimeType.substring(mimeType.lastIndexOf("/") + 1)
    }

    fun decodeBitmap(
        file: File?,
        maxWidth: Int,
        maxHeight: Int,
        byteStorage: ByteArray?,
        options: BitmapFactory.Options?,
        exactDecode: Boolean
    ): Bitmap? {
        var byteStorage = byteStorage
        var options = options
        val `is`: InputStream
        try {
            // In this, we can set the buffer size
            `is` = BufferedInputStream(
                FileInputStream(file),
                byteStorage?.size ?: DEFAULT_BUFFER_SIZE
            )
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
            return null
        }
        if (options == null) options = createOptions() else resetOptions(options)
        // First decode with inJustDecodeBounds=true to check dimensions
        options.inJustDecodeBounds = true
        // 5MB. This is the max image header size we can handle, we preallocate a much smaller buffer
        // but will resize up to this amount if necessary.
        `is`.mark(5 * 1024 * 1024)
        BitmapFactory.decodeStream(`is`, null, options)

        // Reset the inputStream
        try {
            `is`.reset()
        } catch (e: IOException) {
            e.printStackTrace()
            StreamUtil.close(`is`)
            resetOptions(options)
            return null
        }
        // Calculate inSampleSize
        calculateScaling(options, maxWidth, maxHeight, exactDecode)
        // Init the BitmapFactory.Options.inTempStorage value
        if (byteStorage == null) byteStorage = ByteArray(DEFAULT_BUFFER_SIZE)
        options.inTempStorage = byteStorage
        // Decode bitmap with inSampleSize set FALSE
        options.inJustDecodeBounds = false
        var bitmap = BitmapFactory.decodeStream(`is`, null, options)
        // Close the Stream
        StreamUtil.close(`is`)
        // And Reset the option
        resetOptions(options)
        // To scale bitmap to user set
        bitmap = scaleBitmap(bitmap!!, maxWidth, maxHeight, true)
        return bitmap
    }

    /**
     * 按长宽比缩小一个Bitmap
     *
     * @param source        待缩小的[Bitmap]
     * @param scale         缩放比0～1，1代表不缩放
     * @param recycleSource 是否释放Bitmap源
     * @return 一个缩小后的Bitmap
     */
    fun scaleBitmap(
        source: Bitmap,
        scale: Float,
        recycleSource: Boolean
    ): Bitmap {
        if (scale <= 0 || scale >= 1) return source
        val m = Matrix()
        val width = source.width
        val height = source.height
        m.setScale(scale, scale)
        val scaledBitmap = Bitmap.createBitmap(source, 0, 0, width, height, m, false)
        if (recycleSource) source.recycle()
        return scaledBitmap
    }

    /**
     * 按照长宽比缩小一个Bitmap到指定尺寸，
     * 当传入的高宽都大于原始值时将不做缩小操作
     *
     * @param source          待缩小的[Bitmap]
     * @param targetMaxWidth  目标宽度
     * @param targetMaxHeight 目标高度
     * @param recycleSource   是否释放Bitmap源
     * @return 一个缩小后的Bitmap
     */
    fun scaleBitmap(
        source: Bitmap,
        targetMaxWidth: Int,
        targetMaxHeight: Int,
        recycleSource: Boolean
    ): Bitmap? {
        val sourceWidth = source.width
        val sourceHeight = source.height
        var scaledBitmap = source
        if (sourceWidth > targetMaxWidth || sourceHeight > targetMaxHeight) {
            val minScale = Math.min(
                targetMaxWidth / sourceWidth.toFloat(),
                targetMaxHeight / sourceHeight.toFloat()
            )
            scaledBitmap = Bitmap.createScaledBitmap(
                scaledBitmap,
                (sourceWidth * minScale).toInt(),
                (sourceHeight * minScale).toInt(), false
            )
            if (recycleSource) source.recycle()
        }
        return scaledBitmap
    }

    /**
     * 通过[BitmapFactory.Options]计算图片的缩放比,
     * 并将缩放后的信息存储在传入的[BitmapFactory.Options]中，
     * 以便后续的 [BitmapFactory.decodeStream]等操作
     *
     * @param options            一个图片的[BitmapFactory.Options]， 含有图片的基础信息
     * @param requestedMaxWidth  目标宽度
     * @param requestedMaxHeight 目标高度
     * @param exactDecode        是否精确计算，该参数只在 [Build.VERSION.SDK_INT] 大于 Api19 时有效
     */
    private fun calculateScaling(
        options: BitmapFactory.Options,
        requestedMaxWidth: Int,
        requestedMaxHeight: Int,
        exactDecode: Boolean
    ): BitmapFactory.Options? {
        val sourceWidth = options.outWidth
        val sourceHeight = options.outHeight
        if (sourceWidth <= requestedMaxWidth && sourceHeight <= requestedMaxHeight) {
            return options
        }
        val maxFloatFactor = Math.max(
            sourceHeight / requestedMaxHeight.toFloat(),
            sourceWidth / requestedMaxWidth.toFloat()
        )
        val maxIntegerFactor = Math.floor(maxFloatFactor.toDouble()).toInt()
        val lesserOrEqualSampleSize =
            Math.max(1, Integer.highestOneBit(maxIntegerFactor))
        options.inSampleSize = lesserOrEqualSampleSize
        // Density scaling is only supported if inBitmap is null prior to KitKat. Avoid setting
        // densities here so we calculate the final Bitmap size correctly.
        if (exactDecode && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            val scaleSize = sourceWidth / lesserOrEqualSampleSize.toFloat()
            val outSize = sourceWidth / maxFloatFactor
            options.inTargetDensity = 1000
            options.inDensity = (1000 * (scaleSize / outSize) + 0.5).toInt()

            // If isScaling
            if (options.inTargetDensity != options.inDensity) {
                options.inScaled = true
            } else {
                options.inTargetDensity = 0
                options.inDensity = options.inTargetDensity
            }
        }
        return options
    }

    fun compressImage(
        sourceFile: File, maxSize: Long,
        minQuality: Int, maxWidth: Int,
        maxHeight: Int
    ): File? {
        return compressImage(
            sourceFile,
            maxSize,
            minQuality,
            maxWidth,
            maxHeight,
            true
        )
    }

    fun compressImage(
        sourceFile: File, maxSize: Long,
        minQuality: Int, maxWidth: Int,
        maxHeight: Int, exactDecode: Boolean
    ): File? {
        return compressImage(
            sourceFile,
            maxSize,
            minQuality,
            maxWidth,
            maxHeight,
            null,
            null,
            exactDecode
        )
    }

    /**
     * 压缩图片
     *
     * @param sourceFile  原图地址
     * @param maxSize     最大文件地址byte
     * @param minQuality  最小质量
     * @param maxWidth    最大宽度
     * @param maxHeight   最大高度
     * @param byteStorage 用于批量压缩时的buffer，不必要为null，
     * 需要时，推荐 {[.DEFAULT_BUFFER_SIZE]}
     * @param options     批量压缩时复用参数，可调用 {[.createOptions]} 得到
     * @param exactDecode 是否精确解码， TRUE： 在4.4及其以上机器中能更节约内存
     * @return 返回压缩后的图片文件，该图片存储在原图同级目录下，以compress.temp结尾
     */
    fun compressImage(
        sourceFile: File,
        maxSize: Long,
        minQuality: Int,
        maxWidth: Int,
        maxHeight: Int,
        byteStorage: ByteArray?,
        options: BitmapFactory.Options?,
        exactDecode: Boolean
    ): File? {
        // build source file
        if (!sourceFile.exists() || !sourceFile.canRead()) return null

        // create new temp file
        val tempFile = File(
            sourceFile.parent,
            String.format("compress_%s.temp", System.currentTimeMillis())
        )
        if (!tempFile.exists()) {
            try {
                if (!tempFile.createNewFile()) return null
            } catch (e: IOException) {
                e.printStackTrace()
                return null
            }
        }

        // build to bitmap
        var bitmap =
            decodeBitmap(sourceFile, maxWidth, maxHeight, byteStorage, options, exactDecode)
                ?: return null

        // Get the bitmap format
        val compressFormat =
            if (bitmap.hasAlpha()) CompressFormat.PNG else CompressFormat.JPEG

        // Write to out put file
        var isOk = false
        for (i in 1..10) {
            // In this we change the quality start 92%
            var quality = 92
            while (true) {
                var outputStream: BufferedOutputStream? = null
                try {
                    outputStream = BufferedOutputStream(FileOutputStream(tempFile))
                    bitmap.compress(compressFormat, quality, outputStream)
                } catch (e: IOException) {
                    e.printStackTrace()
                    // on IOException we need recycle the bitmap
                    bitmap.recycle()
                    return null
                } finally {
                    StreamUtil.close(outputStream)
                }
                // Check file size
                val outSize: Long = tempFile.length()
                if (outSize <= maxSize) {
                    isOk = true
                    break
                }
                if (quality < minQuality) break
                quality--
            }
            if (isOk) {
                break
            } else {
                // If not ok, we need scale the Bitmap to small
                // In this, once subtract 2%, most 20%
                bitmap = scaleBitmap(bitmap, 1 - 0.2f * i, true)
            }
        }
        // recycle bitmap
        bitmap.recycle()

        // The end, If not success, return false
        return if (!isOk) null else tempFile

        // Rename to out file
    }
}